// 顶部模块区域
import type { PluginManifest, PluginContext, PluginAPI } from '../modules/plugin/types';
import { commandService } from './commandService.ts'; // <-- 添加 .ts 扩展名
import * as editorExt from './editorExtensionService';
import { invoke } from '@tauri-apps/api/core';
import { useWorkspaceStore } from '../stores/workspace';
import { getAppSetting, setAppSetting } from '../tauriApi';

// Detect Tauri runtime
const isTauri = typeof window !== 'undefined' && !!(window as any).__TAURI_INTERNALS__;

// 语言激活映射：languageId -> 待激活插件列表
const languageActivationMap = new Map<string, { manifest: PluginManifest; mainFilePath: string }[]>();

const loadedPlugins = new Map<string, { module: any, context: PluginContext, manifest: PluginManifest }>();
const pendingPlugins = new Map<string, { manifest: PluginManifest; mainFilePath: string; activated: boolean }>();

// 记录运行时插件目录：name -> dir
const runtimePluginDirs = new Map<string, string>();

// 使用 Vite 的 glob 生成器来加载内置插件的入口模块（ts/js），避免在开发环境下出现 MIME 或域名不匹配问题
const builtinModuleLoaders: Record<string, () => Promise<any>> = (import.meta as any).glob('/src/plugins/built-in/**/*.{ts,js}');

async function activatePlugin(manifest: PluginManifest, pluginModule: any) {
    const context: PluginContext = { subscriptions: [] };

    // Create a tailored API for this specific plugin
    const api: PluginAPI = {
        commands: {
            registerCommand(commandId: string, callback: (...args: any[]) => any): { dispose: () => void } {
                // Find the command's static definition in the manifest to get its title
                const commandInfo = manifest.contributes?.commands?.find(c => c.command === commandId);
                const title = commandInfo?.title || commandId;
                return commandService.register(commandId, title, callback);
            },
            executeCommand<T>(commandId: string, ...args: any[]): Promise<T> {
                return commandService.execute(commandId, ...args);
            }
        },
        window: {
            showInformationMessage(message: string): void {
                alert(message); // Placeholder, we'll improve this later
            }
        },
        workspace: {
            get rootPath() {
                try {
                    const ws = useWorkspaceStore();
                    return ws.workspaceRoot || '';
                } catch {
                    return '';
                }
            }
        },
        rpc: {
            async invoke<T>(command: string, params: any): Promise<T> {
                console.log(`Invoking RPC: ${command}`, params);
                try {
                    return await invoke(command, params) as T;
                } catch (error) {
                    console.error(`RPC call failed: ${command}`, error);
                    throw error;
                }
            }
        },

        // 新增：编辑器 / 语言 / 主题 API
        editor: {
            registerAction(action) {
                return editorExt.registerAction(action);
            }
        },
        languages: {
            registerCompletionProvider: editorExt.registerCompletionProvider,
            // 新增：内联补全提供者
            registerInlineCompletionsProvider: editorExt.registerInlineCompletionsProvider,
            registerHoverProvider: editorExt.registerHoverProvider,
            registerCodeActionProvider: editorExt.registerCodeActionProvider,
            registerDocumentFormattingEditProvider: editorExt.registerDocumentFormattingEditProvider,
            registerDocumentRangeFormattingEditProvider: editorExt.registerDocumentRangeFormattingEditProvider,
            registerDefinitionProvider: editorExt.registerDefinitionProvider,
            registerReferenceProvider: editorExt.registerReferenceProvider,
            registerRenameProvider: editorExt.registerRenameProvider,
            registerLanguage: editorExt.registerLanguage,
        },
        themes: {
            registerTheme: editorExt.registerTheme,
            setTheme: editorExt.setTheme,
        },
    };

    // Call the plugin's activate function with its context and tailored API
    pluginModule.activate(context, api);
    loadedPlugins.set(manifest.name, { module: pluginModule, context, manifest });
    console.log(`Activated plugin: ${manifest.name}`);
}

function resolveBuiltinLoader(path: string): (() => Promise<any>) | undefined {
    let loader = builtinModuleLoaders[path];
    if (loader) return loader;
    // 兼容 ts/js 扩展名差异
    if (path.endsWith('.ts')) loader = builtinModuleLoaders[path.replace(/\.ts$/, '.js')];
    if (!loader && path.endsWith('.js')) loader = builtinModuleLoaders[path.replace(/\.js$/, '.ts')];
    return loader;
}

async function ensureActivated(manifest: PluginManifest, mainFilePath: string) {
    if (loadedPlugins.has(manifest.name)) return;
    try {
        // 通过 Vite 预生成的 loader 导入，避免原始 URL 导致的 MIME 类型错误
        const loader = resolveBuiltinLoader(mainFilePath);
        if (!loader) {
            throw new Error(`No loader found for built-in plugin entry: ${mainFilePath}`);
        }
        const pluginModule = await loader();
        if (pluginModule && typeof pluginModule.activate === 'function') {
            await activatePlugin(manifest, pluginModule);
            const pending = pendingPlugins.get(manifest.name);
            if (pending) pending.activated = true;
        } else {
            console.error(`Failed to activate plugin ${manifest.name}: 'activate' function not found.`);
        }
    } catch (error) {
        console.error(`Error activating plugin ${manifest.name}:`, error);
    }
}

// --- Runtime plugin loading helpers ---
function joinPath(base: string, rel: string): string {
    const cleanedRel = rel.replace(/^\.\//, '').replace(/^\/+/, '');
    const sep = /\\|^[A-Za-z]:/.test(base) ? '\\' : '/';
    if (base.endsWith('/') || base.endsWith('\\')) return base + cleanedRel;
    return base + sep + cleanedRel;
}

async function dynamicImportFromCode(jsCode: string) {
    const blob = new Blob([jsCode], { type: 'text/javascript' });
    const url = URL.createObjectURL(blob);
    try {
        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
        // @ts-ignore
        return await import(/* @vite-ignore */ url);
    } finally {
        // Note: do not revoke immediately to keep module alive; safe to keep for session
    }
}

async function loadRuntimePluginFromDirectory(dir: string) {
    try {
        const manifestPath = joinPath(dir, 'plugin.json');
        const manifestText = await invoke<string>('fs_read_text', { path: manifestPath });
        const manifest = JSON.parse(manifestText) as PluginManifest;
        const mainPath = joinPath(dir, (manifest.main || './index.js').replace(/^\.\//, ''));
        const mainCode = await invoke<string>('fs_read_text', { path: mainPath });
        const pluginModule = await dynamicImportFromCode(mainCode);
        if (pluginModule && typeof pluginModule.activate === 'function') {
            await activatePlugin(manifest, pluginModule);
            runtimePluginDirs.set(manifest.name, dir);
            console.log(`Runtime plugin loaded: ${manifest.name}`);
        } else {
            console.error(`Runtime plugin missing 'activate': ${manifest.name}`);
        }
    } catch (e) {
        console.error('Failed to load runtime plugin from', dir, e);
    }
}

export async function loadRuntimePlugins() {
    if (!isTauri) return; // runtime install is only supported in Tauri
    try {
        const list = await getAppSetting('userPlugins');
        if (Array.isArray(list)) {
            for (const dir of list) {
                if (typeof dir === 'string' && dir) {
                    await loadRuntimePluginFromDirectory(dir);
                }
            }
        }
    } catch (e) {
        console.warn('No user runtime plugins found or failed to load registry.', e);
    }
}

export async function installPluginFromDirectory(dir: string) {
    if (!isTauri) throw new Error('Runtime plugin installation only available in Tauri');
    const current = await getAppSetting('userPlugins');
    const list: string[] = Array.isArray(current) ? current : [];
    if (!list.includes(dir)) {
        list.push(dir);
        await setAppSetting('userPlugins', list);
    }
    await loadRuntimePluginFromDirectory(dir);
    return true;
}

export async function uninstallRuntimePlugin(name: string) {
    try {
        const dir = runtimePluginDirs.get(name);
        const settings = await getAppSetting('userPlugins');
        const list: string[] = Array.isArray(settings) ? settings : [];
        const idx = dir ? list.indexOf(dir) : -1;
        if (idx >= 0) {
            list.splice(idx, 1);
            await setAppSetting('userPlugins', list);
        }
        // If loaded, deactivate just this plugin
        const loaded = loadedPlugins.get(name);
        if (loaded) {
            try { if (loaded.module?.deactivate) loaded.module.deactivate(); } catch {}
            try { loaded.context.subscriptions.forEach(s => { try { s.dispose(); } catch {} }); } catch {}
            loadedPlugins.delete(name);
        }
        runtimePluginDirs.delete(name);
        return true;
    } catch (e) {
        console.error('Failed to uninstall runtime plugin', name, e);
        return false;
    }
}

export function listPlugins() {
    const results: Array<{
        id: string;
        name: string;
        displayName: string;
        version: string;
        description: string;
        author: string;
        isActive: boolean;
        activationEvents: string[];
        category: string;
        source: 'built-in' | 'runtime';
        dir?: string;
    }> = [];

    // Built-in pending (not yet active) and active
    for (const [, p] of pendingPlugins) {
        const manifest = p.manifest;
        const active = loadedPlugins.has(manifest.name);
        results.push({
            id: manifest.name,
            name: manifest.name,
            displayName: manifest.displayName,
            version: manifest.version,
            description: manifest.description,
            author: manifest.author,
            isActive: active,
            activationEvents: (manifest.activationEvents as any) || [],
            category: (manifest as any).category || 'general',
            source: 'built-in',
        });
    }

    // Loaded-only that might not be in pending (runtime-loaded)
    for (const [name, p] of loadedPlugins) {
        if (!results.find(r => r.name === name)) {
            const m = p.manifest;
            results.push({
                id: m.name,
                name: m.name,
                displayName: m.displayName,
                version: m.version,
                description: m.description,
                author: m.author,
                isActive: true,
                activationEvents: (m.activationEvents as any) || [],
                category: (m as any).category || 'general',
                source: runtimePluginDirs.has(name) ? 'runtime' : 'built-in',
                dir: runtimePluginDirs.get(name),
            });
        }
    }

    return results;
}

/**
 * 由编辑器在首次使用某语言时调用，用于触发对应插件的 onLanguage 懒激活
 */
export async function notifyLanguageEncountered(languageId: string) {
    const list = languageActivationMap.get(languageId);
    if (!list || !list.length) return;
    // 激活并清空该语言的待激活列表
    languageActivationMap.delete(languageId);
    for (const item of list) {
        await ensureActivated(item.manifest, item.mainFilePath);
    }
}

/**
 * Discovers, loads, and activates all built-in plugins.
 */
export async function loadBuiltinPlugins() {
    const manifests = (import.meta as any).glob('/src/plugins/built-in/*/plugin.json');

    for (const path in manifests) {
      try {
        const manifestModule = await manifests[path]();
        const manifest: PluginManifest = (manifestModule as any).default;
        const pluginPath = path.substring(0, path.lastIndexOf('/'));
        console.log(`Found plugin: ${manifest.name}`);

        const mainFilePath = `${pluginPath}/${manifest.main.replace('./', '')}`;

        // 记录到 pending 列表
        pendingPlugins.set(manifest.name, { manifest, mainFilePath, activated: false });

        const events = (manifest.activationEvents || []) as unknown as string[];

        // onStartup: 立即激活
        if (events.includes('onStartup')) {
          await ensureActivated(manifest, mainFilePath);
          continue;
        }

        // onCommand: 为所有贡献的命令注册“代理命令”，执行时再激活插件
        if (events.includes('onCommand') && manifest.contributes?.commands?.length) {
          for (const cmd of manifest.contributes.commands) {
            const id = cmd.command;
            const title = cmd.title || id;

            // 如果已存在（可能被其他地方注册），跳过
            // 否则注册代理
            commandService.register(id, title, async (...args: any[]) => {
              // 确保插件已激活
              await ensureActivated(manifest, mainFilePath);
              // 插件激活后，真实命令已由插件用相同 id 覆盖注册
              return commandService.execute(id, ...args);
            });
            console.log(`Registered proxy command for lazy activation: ${id} ('${title}')`);
          }
          // 继续检查 onLanguage
        }

        // onLanguage: 收集 "onLanguage:xxx" 映射，首次遇到该语言时触发
        for (const ev of events) {
          if (typeof ev === 'string' && ev.startsWith('onLanguage:')) {
            const lang = ev.split(':')[1]?.trim();
            if (lang) {
              const arr = languageActivationMap.get(lang) || [];
              arr.push({ manifest, mainFilePath });
              languageActivationMap.set(lang, arr);
              console.log(`Registered onLanguage lazy activation: ${manifest.name} -> ${lang}`);
            }
          }
        }

        // 其他激活事件可继续扩展
      } catch (error) {
        console.error(`Error loading plugin from path ${path}:`, error);
      }
    }
  }

/**
 * Deactivates all loaded plugins.
 */
export function deactivateAllPlugins() {
    for (const [name, plugin] of loadedPlugins.entries()) {
        if (plugin.module && typeof plugin.module.deactivate === 'function') {
            plugin.module.deactivate();
        }
        // Dispose all subscriptions
        plugin.context.subscriptions.forEach((sub: { dispose: () => any; }) => sub.dispose());
        console.log(`Deactivated plugin: ${name}`);
    }
    loadedPlugins.clear();
}